﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace MotionDetect
{
    internal static class DataManager
    {
        //根据二维坐标获取一维坐标，通道数为3
        public static int GetIndex(int x, int y, int w) { return (y * w + x) * 3; }
        /// <summary>
        /// 将二维坐标映射为一维坐
        /// </summary>
        /// <param name="x">水平坐标</param>
        /// <param name="y">垂直坐标</param>
        /// <param name="w">水平宽度</param>
        /// <param name="c">水平通道数，如BGR为3通道</param>
        /// <returns></returns>
        public static int GetIndex(int x, int y, int w, int c) { return (y * w + x) * c; }

        //二维字节数组上绘制矩形
        unsafe public static void DrawRectangle(Rectangle rect, Color color, byte* pBuffer, Size sizeBuffer)
        {
            for (int i = rect.Left; i < rect.Right; i++)
            {
                int indexTop = GetIndex(i, rect.Top, sizeBuffer.Width);
                int indexBottom = GetIndex(i, rect.Bottom - 1, sizeBuffer.Width);
                pBuffer[indexTop + 0] = color.B;
                pBuffer[indexTop + 1] = color.G;
                pBuffer[indexTop + 2] = color.R;
                pBuffer[indexBottom + 0] = color.B;
                pBuffer[indexBottom + 1] = color.G;
                pBuffer[indexBottom + 2] = color.R;
            }

            for (int i = rect.Top; i < rect.Bottom; i++)
            {
                int indexLeft = GetIndex(rect.Left, i, sizeBuffer.Width);
                int indexRight = GetIndex(rect.Right - 1, i, sizeBuffer.Width);
                pBuffer[indexLeft + 0] = color.B;
                pBuffer[indexLeft + 1] = color.G;
                pBuffer[indexLeft + 2] = color.R;
                pBuffer[indexRight + 0] = color.B;
                pBuffer[indexRight + 1] = color.G;
                pBuffer[indexRight + 2] = color.R;
            }
        }

        public static Random random = new Random();
        //获取一个近似服从标准二维正态分布的随机坐标点
        public static PointF GetRandomNormalDistrbution()
        {
            double u, v, s = 0, x = 0, y = 0, temp;
            do
            {
                u = random.NextDouble();
                v = random.NextDouble();
                u = 2 * u - 1;
                v = 2 * v - 1;
                s = u * u + v * v;
            } while (s > 1 || s == 0);
            temp = Math.Sqrt(-2 * Math.Log(s) / s);
            x = temp * u;
            y = temp * v;

            return new PointF(Convert.ToSingle(x), Convert.ToSingle(y));
        }

        //获取指定区域指定数量服从标准二维正态分布的随机坐标点
        public static Point[] GetRandomPoint(Rectangle rect, int count)
        {
            if (rect.Width == 0 || rect.Height == 0) throw new ApplicationException("width or height is zero");
            PointF[] pointfs = new PointF[count];
            Point[] points = new Point[count];
            RectangleF rectZero = new RectangleF(-(float)rect.Width / 2, -(float)rect.Height / 2, rect.Width, rect.Height);
            for (int i = 0; i < count; i++)
            {
                do
                {
                    pointfs[i] = GetRandomNormalDistrbution();
                    pointfs[i].X *= (float)rect.Width / 4;
                    pointfs[i].Y *= (float)rect.Height / 4;
                    pointfs[i].X += (float)rect.Width / 2;
                    pointfs[i].Y += (float)rect.Height / 2;
                } while (pointfs[i].X < 0 || pointfs[i].X >= rect.Width - 1
                        || pointfs[i].Y < 0 || pointfs[i].Y >= rect.Height - 1);

                points[i].X = Convert.ToInt32(rect.X + pointfs[i].X);
                points[i].Y = Convert.ToInt32(rect.Y + pointfs[i].Y);
            }



            return points;
        }

        /// <summary>
        /// 获取一组将区域中连续点包含在其中的矩形框
        /// </summary>
        /// <param name="pOut">二维字节数组指针</param>
        /// <param name="imageOut"></param>
        /// <param name="sizeMin"></param>
        /// <param name="sizeMax"></param>
        /// <returns></returns>
        unsafe public static List<Rectangle> GetExpandRegion(byte* pOut, Size imageOut, Size sizeMin, Size sizeMax)
        {
            List<Rectangle> rectActive = new List<Rectangle>();
            #region 矩形扩张
            for (int yOut = 0; yOut < imageOut.Height; yOut++)
            {
                for (int xOut = 0; xOut < imageOut.Width; xOut++)
                {
                    int index = GetIndex(xOut, yOut, imageOut.Width);
                    if (pOut[index] == 255)
                    {
                        Rectangle rect = new Rectangle(xOut, yOut, 1, 1);
                        //向右延伸
                        for (int i = rect.Right; i < imageOut.Width; i++)
                        {
                            if (pOut[GetIndex(i, yOut, imageOut.Width)] == 0)
                            {
                                rect.Width = i - xOut;
                                break;
                            }
                        }
                        //向下延伸
                        for (int i = rect.Bottom; i < imageOut.Height; i++)
                        {
                            int count = 0;
                            for (int j = rect.Left; j < rect.Right; j++)
                            {
                                if (pOut[GetIndex(j, i, imageOut.Width)] > 0)
                                {
                                    count++;
                                    break;
                                }
                            }
                            if (count == 0)
                            {
                                rect.Height = i - yOut;
                                break;
                            }
                        }
                        //向左延伸
                        for (int i = rect.Left - 1; i > 0; i--)
                        {
                            int count = 0;
                            for (int j = rect.Top; j < rect.Bottom; j++)
                            {
                                if (pOut[GetIndex(i, j, imageOut.Width)] > 0)
                                {
                                    count++;
                                    break;
                                }
                            }
                            if (count == 0)
                            {
                                rect.X = i;
                                rect.Width += rect.Left - i;
                                break;
                            }
                        }
                        //向右延伸
                        for (int i = rect.Right; i < imageOut.Width; i++)
                        {
                            int count = 0;
                            for (int j = rect.Top; j < rect.Bottom; j++)
                            {
                                if (pOut[GetIndex(i, j, imageOut.Width)] > 0)
                                {
                                    count++;
                                    break;
                                }
                            }
                            if (count == 0)
                            {
                                rect.Width = i - rect.Left;
                                break;
                            }
                        }
                        //向下延伸
                        for (int i = rect.Bottom; i < imageOut.Height; i++)
                        {
                            int count = 0;
                            for (int j = rect.Left; j < rect.Right; j++)
                            {
                                if (pOut[GetIndex(j, i, imageOut.Width)] > 0)
                                {
                                    count++;
                                    break;
                                }
                            }
                            if (count == 0)
                            {
                                rect.Height = i - yOut;
                                break;
                            }
                        }

                        if (rect.Width > sizeMin.Width
                            && rect.Height > sizeMin.Height
                            && rect.Width < sizeMax.Width
                            && rect.Height < sizeMax.Height)
                        {
                            rectActive.Add(rect);

                            for (int y = rect.Top; y < rect.Bottom; y++)
                            {
                                for (int x = rect.Left; x < rect.Right; x++)
                                {
                                    //标记运动的点,下次扫描则不再检测该点
                                    int idx = GetIndex(x, y, imageOut.Width);
                                    if (pOut[idx] == 255)
                                    {
                                        pOut[idx] = 0;
                                        pOut[idx + 1] = 255;
                                    }

                                }
                            }
                        }


                    }
                }
            }
            #endregion
            return rectActive;
        }

        //将一组矩形都扩大指定大小，将重叠的矩形合并成一个更大的矩形，直到没有任何两个相互重叠
        public static void UnionRectangle(ref List<Rectangle> rects, Size inflate)
        {
            bool intersectExsit = false;//没有一个相交
            #region 合并相交矩形
            do
            {
                intersectExsit = false;
                for (int i = 0; i < rects.Count - 1; i++)
                {
                    bool intersect = false;//是否与其它相交
                    for (int j = i + 1; j < rects.Count; j++)
                    {
                        Rectangle rect1 = rects[i];
                        Rectangle rect2 = rects[j];
                        rect1.Inflate(inflate);
                        rect2.Inflate(inflate);
                        if (rect1.IntersectsWith(rect2))
                        {
                            rect1.Inflate(-inflate.Width, -inflate.Height);
                            rect2.Inflate(-inflate.Width, -inflate.Height);
                            rects.Remove(rect1);//将重叠的移除
                            rects.Remove(rect2);//将重叠的移除
                            rects.Add(Rectangle.Union(rect1, rect2));//加入合并后的矩形
                            intersect = true;
                            break;
                        }
                    }
                    if (intersect)
                    {
                        intersectExsit = true;
                        break;
                    }
                }

            } while (intersectExsit);
            #endregion
        }

        //获取一组矩形中被包含在指定矩形区域内的矩形
        public static List<Rectangle> ContainRectangle(List<Rectangle> rects, Rectangle rectLimits)
        {
            List<Rectangle> rectContain = new List<Rectangle>();
            foreach (var rect in rects)
            {
                if (rectLimits.Contains(rect)) rectContain.Add(rect);
            }
            return rectContain;
        }

        //获取一组矩形中面积最大的矩形
        public static Rectangle MaxRectangle(List<Rectangle> rects)
        {
            Rectangle rectMax = Rectangle.Empty;

            foreach (var rect in rects)
            {
                if (rectMax == Rectangle.Empty
                    || rect.Width * rect.Height > rectMax.Width * rectMax.Height)
                    rectMax = rect;
            }

            return rectMax;
        }

        //获取特征直方图
        unsafe public static byte[] GetFeatureHistogram(Rectangle rect, byte* pBuffer, Size sizeBuffer, Point[] pointFeature)
        {
            byte[] greyHistogram = new byte[byte.MaxValue + 1];

            foreach (var item in pointFeature)
            {
                byte grey = pBuffer[GetIndex(rect.X + item.X, rect.Y + item.Y, sizeBuffer.Width)];
                greyHistogram[grey]++;
            }

            return greyHistogram;
        }

        unsafe public static double GetSimilarFeature(byte[] feature1, byte[] feature2)
        {
            if (feature1 == null || feature2 == null)
                throw new Exception("feature is null");

            //int sum = 0;
            //for (int i = 0; i < Math.Min(feature1.Length, feature2.Length); i++)
            //{
            //    sum += byte.MaxValue - Math.Abs(feature1[i] - feature2[i]);
            //}
            //return sum;

            double similar = 0;
            int sum1 = 0, sum2 = 0;
            int length = Math.Min(feature1.Length, feature2.Length);
            for (int i = 0; i < length; i++)
            {
                sum1 += feature1[i];
                sum2 += feature2[i];
            }
            for (int i = 0; i < length; i++)
            {
                similar += Math.Sqrt((double)feature1[i] * (double)feature2[i] / sum1 / sum2);
            }

            return similar;
        }

        //将输入图像pIn水平移动x，垂直移动y，存入pOut
        //channelCout表示图像为几通道的，如RGB为3通道，ARGB为4通道
        //则pOut中的(X,Y)点对应pIn中的(X-x,Y-y)点
        unsafe public static void OffSetImage(byte* pIn, byte* pOut, Size sizeIn, Size sizeOut, int x, int y)
        {
            Rectangle rectIn = new Rectangle(0, 0, sizeIn.Width, sizeIn.Height);
            Point pointIn = new Point();
            for (int h = 0; h < sizeOut.Height; h++)
            {
                pointIn = new Point(0, h - y);
                if (!rectIn.Contains(pointIn)) continue;
                for (int w = 0; w < sizeOut.Width; w++)
                {
                    pointIn = new Point(w - x, h - y);
                    int indexOut = GetIndex(w, h, sizeOut.Width);
                    if (!rectIn.Contains(pointIn))
                    {
                        pOut[indexOut] = pOut[indexOut + 1] = pOut[indexOut + 2] = 0;
                    }
                    else
                    {
                        int indexIn = GetIndex(pointIn.X, pointIn.Y, sizeIn.Width);
                        pOut[indexOut] = pOut[indexOut + 1] = pOut[indexOut + 2] = pIn[indexIn];
                    }
                }
            }
        }
    }
}
